using System;
using System.Collections.Generic;
using System.Linq;
using Irony.Ast;
using Irony.Parsing;


namespace Flit
{
    /// <summary>
    /// Represents a transition from one fluent interface state to another. Described in the Flit DSL as <b>x->y</b>, it is usually represented as a method call
    /// declared on class (state) x, returning a new instance of class y
    /// </summary>
    public class Transition : GraphItem, IAstNodeInit
    {
        /// <summary>
        /// Instantiate a new transition. Do not call this directly, let the parser build these
        /// </summary>
        public Transition()
        {
            GenericAttributes = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
        }
        /// <summary>
        /// Called by Irony
        /// </summary>
        /// <param name="context"></param>
        /// <param name="parseNode"></param>
        public void Init(ParsingContext context, ParseTreeNode parseNode)
        {
            FromIdentifier = parseNode.ChildNodes[0].Token.ValueString;
            ToIdentifier = parseNode.ChildNodes[1].Token.ValueString;
            Label = parseNode.Descendants().Select(x => x.AstNode).OfType<Label>().SingleOrDefault() ?? new Label { Name = ToIdentifier };

            var v = parseNode.DescendantsWithTermType(x => x.GenericAttribute);
            foreach (var n in v)
            {
                GenericAttributes[n.ChildNodes[0].Token.ValueString] = n.ChildNodes[1].Token.ValueString;
            }
        }

        /// <summary>
        /// The identifier of the state that this transition leaves (the class that this transition's method is declared on):
        /// <b>x</b>->y[label="foo(int bar)", color=red]
        /// </summary>
        public string FromIdentifier { get; private set; }

        /// <summary>
        /// The identifier of the state that this transition goes to (the class that will be instantiated and returned by this transition's method):
        /// x-><b>y</b>[label="foo(int bar)", color=red]
        /// </summary>
        public string ToIdentifier { get; private set; }

        /// <summary>
        /// The label attribute (or it's inferred value) of this transition: x->y[<b>label="foo(int bar)"</b>, color=red]
        /// </summary>
        public Label Label { get; private set; }

        /// <summary>
        /// All non-label attributes of this transition: x->y[label="foo(int bar), <b>color=red"</b>]
        /// </summary>
        public Dictionary<string, string> GenericAttributes { get; private set; }

        /// <summary>
        /// All the parameters required by this transition's method. Includes this transition's parameters, 
        /// and the mandatory parameters of the target class
        /// </summary>
        public IEnumerable<Parameter> AllParameters
        {
            get
            {
                var targetState = Graph.StateByIdentifier(ToIdentifier);
                return targetState.Label.Parameters.Concat(Label.Parameters);
            }
        }

        #region CodeGen helpers

        /// <summary>
        /// renders the transition as a formal method definition <example>Foo(int bar)</example>
        /// </summary>
        public string AsFormalMethodDefinition
        {
            get
            {
                var csv = AllParameters.Select(x => x.AsParameterDeclaration).ToArray();
                return "{0}({1})".InvariantFormat(Label.Name, string.Join(", ", csv));
            }
        }

        /// <summary>
        /// renders the transition as a method call <example>Foo(bar)</example>
        /// </summary>
        public string AsMethodCall
        {
            get
            {
                var csv = AllParameters.Select(x => x.Name).ToArray();
                return "{0}({1})".InvariantFormat(Label.Name, string.Join(", ", csv));
            }
        }

        /// <summary>
        /// The source state for this transition (pulled from the graph)
        /// </summary>
        public State FromState
        {
            get
            {
                return Graph.StateByIdentifier(FromIdentifier);
            }
        }

        /// <summary>
        /// The target state for this transition (pulled from the graph)
        /// </summary>
        public State ToState
        {
            get
            {
                return Graph.StateByIdentifier(ToIdentifier);
            }
        }


        #endregion

    }
}